/*
 * @Author: gongqinying (eycodes@gmail.com)
 * @Date: 2025-08-18 14:13:45
 * @LastEditTime: 2025-08-29 14:19:05
 * @LastEditors: 老年UI仔 dinhy2004@gmail.com
 * @Description: web平台解压加载资源
 */

import { SingletonMgr } from "./SingtonClass";


declare const JSZip: any;

// 资源自动释放设置
const AUTO_RELEASE_INTERVAL = 5 * 60 * 1000; // 每5分钟检查
const AUTO_RELEASE_IDLE_TIME = 10 * 60 * 1000; // 空闲10分钟释放
const MAX_CACHE_SIZE = 50 * 1024 * 1024; // 最大缓存50MB
const MAX_CONCURRENT_DOWNLOADS = 3; // 最大并发下载数
const DOWNLOAD_RETRY_COUNT = 3; // 下载重试次数
const DOWNLOAD_RETRY_DELAY = 1000; // 重试延迟(毫秒)

// 支持的文件类型
const SUPPORTED_EXTENSIONS = ['.json', '.png', '.plist', '.mp3', '.ogg', '.wav'] as const;
type SupportedExtension = typeof SUPPORTED_EXTENSIONS[number];

// 下载状态枚举
enum DownloadStatus {
    PENDING = 'pending',
    DOWNLOADING = 'downloading', 
    COMPLETED = 'completed',
    FAILED = 'failed',
    CANCELLED = 'cancelled'
}

// 下载任务接口
interface DownloadTask {
    bundleName: string;
    url: string;
    status: DownloadStatus;
    progress: number;
    retryCount: number;
    xhr?: XMLHttpRequest;
    promise?: Promise<void>;
}

class ZipBundleLoader extends SingletonMgr {
    constructor() {
        super();
        this.initializeDefaultHandlers();
        this.registerGlobalZipLoaders();
        this.startAutoReleaseTimer();
    }

    // 全局缓存：bundleName → JSZip对象
    // @ts-ignore
    bundleZipMap: Record<string, JSZip> = {};
    // 全局缓存：bundleName → 已解压的文件缓存（资源名 → Uint8Array）
    bundleFileMaps: Record<string, Record<string, { data: Uint8Array; lastAccess: number; size: number }>> = {};
    // 下载任务管理
    downloadTasks: Map<string, DownloadTask> = new Map();
    // 当前并发下载数
    currentDownloads = 0;
    // 当前缓存大小
    currentCacheSize = 0;
    // 自动释放定时器
    autoReleaseTimer?: number;

    // 默认下载器引用
    defaultHandlers: Record<SupportedExtension, Function> = {} as any;

    // 初始化默认下载器
    private initializeDefaultHandlers() {
        SUPPORTED_EXTENSIONS.forEach(ext => {
            // @ts-ignore
            this.defaultHandlers[ext] = cc.assetManager.downloader._downloaders[ext];
        });
    }

    // 启动自动释放定时器
    private startAutoReleaseTimer() {
        this.autoReleaseTimer = setInterval(() => {
            this.performAutoRelease();
        }, AUTO_RELEASE_INTERVAL) as any;
    }

    // 执行自动释放
    private performAutoRelease() {
        const now = Date.now();
        let releasedSize = 0;
        
        for (const bundle in this.bundleFileMaps) {
            for (const key in this.bundleFileMaps[bundle]) {
                const fileCache = this.bundleFileMaps[bundle][key];
                if (fileCache && fileCache.data && now - fileCache.lastAccess > AUTO_RELEASE_IDLE_TIME) {
                    console.log(`[ZipLoader] Auto release file: ${bundle}/${key} (${this.formatBytes(fileCache.size)})`);
                    releasedSize += fileCache.size;
                    this.currentCacheSize -= fileCache.size;
                    fileCache.data = null as any;
                }
            }
        }
        
        if (releasedSize > 0) {
            console.log(`[ZipLoader] Auto release completed: ${this.formatBytes(releasedSize)} freed, current cache: ${this.formatBytes(this.currentCacheSize)}`);
        }
    }

    // 检查缓存限制
    private checkCacheLimit() {
        if (this.currentCacheSize > MAX_CACHE_SIZE) {
            console.warn(`[ZipLoader] Cache size exceeded limit: ${this.formatBytes(this.currentCacheSize)}/${this.formatBytes(MAX_CACHE_SIZE)}`);
            this.forceCacheCleanup();
        }
    }

    // 强制缓存清理
    private forceCacheCleanup() {
        const entries: Array<{bundle: string; key: string; lastAccess: number; size: number}> = [];
        
        // 收集所有缓存条目
        for (const bundle in this.bundleFileMaps) {
            for (const key in this.bundleFileMaps[bundle]) {
                const fileCache = this.bundleFileMaps[bundle][key];
                if (fileCache && fileCache.data) {
                    entries.push({
                        bundle,
                        key,
                        lastAccess: fileCache.lastAccess,
                        size: fileCache.size
                    });
                }
            }
        }
        
        // 按最后访问时间排序，释放最久未使用的
        entries.sort((a, b) => a.lastAccess - b.lastAccess);
        
        let releasedSize = 0;
        for (const entry of entries) {
            if (this.currentCacheSize - releasedSize <= MAX_CACHE_SIZE * 0.8) break; // 释放到80%
            
            const fileCache = this.bundleFileMaps[entry.bundle][entry.key];
            if (fileCache && fileCache.data) {
                console.log(`[ZipLoader] Force release file: ${entry.bundle}/${entry.key} (${this.formatBytes(entry.size)})`);
                releasedSize += entry.size;
                fileCache.data = null as any;
            }
        }
        
        this.currentCacheSize -= releasedSize;
        console.log(`[ZipLoader] Force cleanup completed: ${this.formatBytes(releasedSize)} freed`);
    }

    // 获取URL中的bundle名和相对路径
    private parseAssetUrl(url: string): {bundleName: string; relative: string} | null {
        const match = url.match(/assets\/([^/]+)\/(.+)/);
        if (match) {
            const [_, bundleName, relative] = match;
            return { bundleName, relative };
        }
        return null;
    }

    // 格式化字节大小显示
    formatBytes(bytes: number): string {
        if (bytes === 0) return '0 B';
        const k = 1024;
        const sizes = ['B', 'KB', 'MB', 'GB'];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }

    // HTTP请求获取ArrayBuffer（带进度监控、重试和取消功能）
    httpRequestArrayBuffer(url: string, retryCount = 0): Promise<ArrayBuffer> {
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open('GET', url, true);
            xhr.responseType = 'arraybuffer';
            
            // 保存xhr对象以便取消
            const task = this.downloadTasks.get(url);
            if (task) {
                task.xhr = xhr;
            }
            
            // 下载进度监控
            xhr.onprogress = (event) => {
                if (task) {
                    task.progress = event.lengthComputable ? (event.loaded / event.total) * 100 : 0;
                }
                
                if (event.lengthComputable) {
                    const percentComplete = (event.loaded / event.total) * 100;
                    console.log(`[ZipLoader] 下载进度: ${url} - ${percentComplete.toFixed(1)}% (${this.formatBytes(event.loaded)}/${this.formatBytes(event.total)})`);
                } else {
                    console.log(`[ZipLoader] 下载中: ${url} - 已下载 ${this.formatBytes(event.loaded)}`);
                }
            };
            
            xhr.onload = () => {
                this.currentDownloads--;
                if (task) {
                    task.status = DownloadStatus.COMPLETED;
                    task.progress = 100;
                }
                
                if (xhr.status === 200) {
                    console.log(`[ZipLoader] 下载完成: ${url} - ${this.formatBytes(xhr.response.byteLength)}`);
                    resolve(xhr.response);
                } else {
                    console.error(`[ZipLoader] HTTP请求失败: ${xhr.status} ${xhr.statusText}`);
                    this.handleDownloadError(url, new Error(`HTTP请求失败: ${xhr.status} ${xhr.statusText}`), retryCount, resolve, reject);
                }
            };
            
            xhr.onerror = () => {
                this.currentDownloads--;
                if (task) {
                    task.status = DownloadStatus.FAILED;
                }
                console.error(`[ZipLoader] 网络错误: ${url}`);
                this.handleDownloadError(url, new Error(`网络错误: 无法加载 ${url}`), retryCount, resolve, reject);
            };
            
            xhr.ontimeout = () => {
                this.currentDownloads--;
                if (task) {
                    task.status = DownloadStatus.FAILED;
                }
                console.error(`[ZipLoader] 请求超时: ${url}`);
                this.handleDownloadError(url, new Error(`请求超时: ${url}`), retryCount, resolve, reject);
            };
            
            xhr.onabort = () => {
                this.currentDownloads--;
                if (task) {
                    task.status = DownloadStatus.CANCELLED;
                }
                console.log(`[ZipLoader] 下载已取消: ${url}`);
                reject(new Error(`下载已取消: ${url}`));
            };
            
            xhr.timeout = 30000; // 30秒超时
            console.log(`[ZipLoader] 开始下载: ${url}${retryCount > 0 ? ` (重试第${retryCount}次)` : ''}`);
            this.currentDownloads++;
            xhr.send();
        });
    }
    
    // 处理下载错误和重试
    private handleDownloadError(
        url: string, 
        error: Error, 
        retryCount: number, 
        resolve: (value: ArrayBuffer) => void, 
        reject: (reason: any) => void
    ) {
        if (retryCount < DOWNLOAD_RETRY_COUNT) {
            console.warn(`[ZipLoader] 下载失败，${DOWNLOAD_RETRY_DELAY}ms后重试: ${url}`);
            setTimeout(() => {
                this.httpRequestArrayBuffer(url, retryCount + 1)
                    .then(resolve)
                    .catch(reject);
            }, DOWNLOAD_RETRY_DELAY * (retryCount + 1)); // 指数退避
        } else {
            console.error(`[ZipLoader] 下载失败，已达到最大重试次数: ${url}`);
            reject(error);
        }
    }
    
    // 取消下载
    cancelDownload(bundleName: string): boolean {
        const task = this.downloadTasks.get(bundleName);
        if (task && task.xhr && task.status === DownloadStatus.DOWNLOADING) {
            task.xhr.abort();
            this.downloadTasks.delete(bundleName);
            console.log(`[ZipLoader] 取消下载: ${bundleName}`);
            return true;
        }
        return false;
    }

    // 工具函数：从缓存或zip里取文件（优化版）
    async getFileData(bundleName: string, relative: string): Promise<Uint8Array | null> {
        // 优先从已解压缓存取
        let fileCache = this.bundleFileMaps[bundleName]?.[relative];
        
        if (fileCache && fileCache.data) {
            // 更新最后访问时间
            fileCache.lastAccess = Date.now();
            return fileCache.data;
        }

        const zip = this.bundleZipMap[bundleName];
        if (zip) {
            const zipFile = zip.file(relative);
            if (zipFile) {
                try {
                    const fileData = new Uint8Array(await zipFile.async('uint8array'));
                    const fileSize = fileData.byteLength;
                    
                    // 检查缓存限制
                    if (this.currentCacheSize + fileSize > MAX_CACHE_SIZE) {
                        this.forceCacheCleanup();
                    }
                    
                    // 建立缓存
                    if (!this.bundleFileMaps[bundleName]) {
                        this.bundleFileMaps[bundleName] = {};
                    }
                    
                    this.bundleFileMaps[bundleName][relative] = { 
                        data: fileData, 
                        lastAccess: Date.now(),
                        size: fileSize
                    };
                    
                    this.currentCacheSize += fileSize;
                    console.log(`[ZipLoader] 文件已缓存: ${bundleName}/${relative} (${this.formatBytes(fileSize)}), 总缓存: ${this.formatBytes(this.currentCacheSize)}`);
                    
                    return fileData;
                } catch (error) {
                    console.error(`[ZipLoader] 解压文件失败: ${bundleName}/${relative}`, error);
                    return null;
                }
            }
        }
        return null;
    }

    // 注册全局下载器（只执行一次）
    registerGlobalZipLoaders() {
        // JSON
        cc.assetManager.downloader.register('.json', async (url, options, onComplete) => {
            const match = url.match(/assets\/([^/]+)\/(.+)/);
            if (match) {
                const [_, bundleName, relative] = match;
                const fileData = await this.getFileData(bundleName, relative);
                if (fileData) {
                    let text = new TextDecoder().decode(fileData);
                    if (text.charCodeAt(0) === 0xFEFF) text = text.slice(1); // 去BOM
                    try {
                        return onComplete(null!, JSON.parse(text));
                    } catch {
                        console.warn(`JSON parse failed: ${relative}`);
                        return onComplete(null!, text);
                    }
                }
            };

            this.defaultHandlers['.json'](url, options, onComplete);
        });

        // PNG
        cc.assetManager.downloader.register('.png', async (url, options, onComplete) => {
            const match = url.match(/assets\/([^/]+)\/(.+)/);
            if (match) {
                const [_, bundleName, relative] = match;
                const fileData: any = await this.getFileData(bundleName, relative);
                if (fileData) {
                    const blob = new Blob([fileData], { type: 'image/png' });
                    const img = new Image();
                    img.crossOrigin = 'anonymous';
                    img.onload = () => onComplete(null!, img);
                    img.onerror = () => onComplete(new Error(`Failed to load image: ${relative}`), null);
                    img.src = URL.createObjectURL(blob);
                    return;
                }
            }
            this.defaultHandlers['.png'](url, options, onComplete);
        });

        // Plist
        cc.assetManager.downloader.register('.plist', async (url, options, onComplete) => {
            const match = url.match(/assets\/([^/]+)\/(.+)/);
            if (match) {
                const [_, bundleName, relative] = match;
                const fileData = await this.getFileData(bundleName, relative);
                if (fileData) {
                    return onComplete(null!, new TextDecoder().decode(fileData));
                }
            }
            this.defaultHandlers['.plist'](url, options, onComplete);
        });

        // 音频
        const audioExts = ['.mp3', '.ogg', '.wav'];
        audioExts.forEach(ext => {
            cc.assetManager.downloader.register(ext, async (url, options, onComplete) => {
                const match = url.match(/assets\/([^/]+)\/(.+)/);
                if (match) {
                    const [_, bundleName, relative] = match;
                    const fileData = await this.getFileData(bundleName, relative);
                    if (fileData) {
                        return onComplete(null!, fileData.buffer); // ArrayBuffer
                    }
                }
                this.defaultHandlers[ext](url, options, onComplete);
            });
        });
    }

    // 从远程加载并注册 zip bundle（优化版）
    async loadZipBundle(bundleName: string): Promise<void> {
        // 检查是否已在下载队列中
        const existingTask = this.downloadTasks.get(bundleName);
        if (existingTask) {
            if (existingTask.status === DownloadStatus.DOWNLOADING) {
                console.log(`[ZipLoader] bundle '${bundleName}' 正在下载中，等待中...`);
                return existingTask.promise || Promise.resolve();
            } else if (existingTask.status === DownloadStatus.COMPLETED) {
                console.log(`[ZipLoader] bundle '${bundleName}' 已下载完成`);
                return Promise.resolve();
            }
        }

        // 如果已解压缓存存在，直接跳过下载和解压
        if (this.bundleZipMap[bundleName]) {
            console.log(`[ZipLoader] bundle '${bundleName}' 已在内存中，跳过解压`);
            return this.ensureBundleLoaded(bundleName);
        }

        // 检查并发限制
        if (this.currentDownloads >= MAX_CONCURRENT_DOWNLOADS) {
            console.log(`[ZipLoader] 达到最大并发下载数，等待中...`);
            await this.waitForDownloadSlot();
        }

        // 获取bundle版本信息
        // @ts-ignore
        const bundleVers = cc.assetManager.downloader.bundleVers;
        if (!bundleVers) {
            // throw new Error('无法获取bundle版本信息');
            return this.ensureBundleLoaded(bundleName);
        }

        const md5key: string = bundleVers[bundleName];
        if (!md5key) {
            // throw new Error(`未找到bundle '${bundleName}' 的版本信息`);
            return this.ensureBundleLoaded(bundleName);
        }

        const zipUrl = `assets/${bundleName}/${bundleName}.${md5key}.zip`;
        
        // 创建下载任务
        const downloadTask: DownloadTask = {
            bundleName,
            url: zipUrl,
            status: DownloadStatus.DOWNLOADING,
            progress: 0,
            retryCount: 0
        };
        
        this.downloadTasks.set(bundleName, downloadTask);
        
        console.log(`[ZipLoader] 正在下载并解压 bundle '${bundleName}'...`);
        
        try {
            // 下载和解压
            const downloadPromise = this.downloadAndExtractBundle(zipUrl, bundleName)
                .then(() => void 0); // 转换为Promise<void>
            downloadTask.promise = downloadPromise;
            
            await downloadPromise;
            
            downloadTask.status = DownloadStatus.COMPLETED;
            console.log(`[ZipLoader] bundle '${bundleName}' 下载和解压完成`);
            
            // 加载bundle
            await this.ensureBundleLoaded(bundleName);
            
        } catch (error) {
            downloadTask.status = DownloadStatus.FAILED;
            this.downloadTasks.delete(bundleName);
            
            console.error(`[ZipLoader] bundle '${bundleName}' 加载失败:`, error);
            
            // 如果zip下载失败，尝试使用默认加载方式
            console.log(`[ZipLoader] 尝试使用默认方式加载 bundle '${bundleName}'`);
            return this.ensureBundleLoaded(bundleName);
        }
    }
    
    // 下载和解压bundle
    private async downloadAndExtractBundle(zipUrl: string, bundleName: string): Promise<ArrayBuffer> {
        const arrayBuffer = await this.httpRequestArrayBuffer(zipUrl);
        
        try {
            const zip = await JSZip.loadAsync(arrayBuffer);
            this.bundleZipMap[bundleName] = zip;
            this.bundleFileMaps[bundleName] = {}; // 初始化缓存
            console.log(`[ZipLoader] bundle '${bundleName}' ZIP解析成功`);
            return arrayBuffer;
        } catch (error) {
            console.error(`[ZipLoader] ZIP解析失败: ${bundleName}`, error);
            throw new Error(`ZIP解析失败: ${error.message}`);
        }
    }
    
    // 确保 bundle 已加载
    private ensureBundleLoaded(bundleName: string): Promise<void> {
        return new Promise<void>((resolve, reject) => {
            if (!cc.assetManager.getBundle(bundleName)) {
                cc.assetManager.loadBundle(bundleName, err => {
                    if (err) {
                        console.error(`[ZipLoader] 加载bundle失败: ${bundleName}`, err);
                        return reject(err);
                    }
                    console.log(`[ZipLoader] bundle '${bundleName}' 已成功加载`);
                    resolve();
                });
            } else {
                console.log(`[ZipLoader] bundle '${bundleName}' 已存在`);
                resolve();
            }
        });
    }
    
    // 等待下载槽位
    private async waitForDownloadSlot(): Promise<void> {
        return new Promise(resolve => {
            const checkSlot = () => {
                if (this.currentDownloads < MAX_CONCURRENT_DOWNLOADS) {
                    resolve();
                } else {
                    setTimeout(checkSlot, 100); // 100ms后再次检查
                }
            };
            checkSlot();
        });
    }

    // 释放 bundle 缓存 & zip 数据（优化版）
    unloadZipBundle(bundleName: string): boolean {
        let releasedSize = 0;
        
        // 取消正在进行的下载
        this.cancelDownload(bundleName);
        
        // 释放 Cocos 资源
        const bundle = cc.assetManager.getBundle(bundleName);
        if (bundle) {
            bundle.releaseAll();
            cc.assetManager.removeBundle(bundle);
            console.log(`[ZipLoader] 已释放 Cocos bundle: ${bundleName}`);
        }
        
        // 计算并释放文件缓存
        const fileCache = this.bundleFileMaps[bundleName];
        if (fileCache) {
            for (const key in fileCache) {
                const file = fileCache[key];
                if (file && file.data && file.size) {
                    releasedSize += file.size;
                }
            }
            delete this.bundleFileMaps[bundleName];
        }
        
        // 释放 ZIP 对象
        const zipExists = !!this.bundleZipMap[bundleName];
        delete this.bundleZipMap[bundleName];
        
        // 更新缓存统计
        this.currentCacheSize -= releasedSize;
        
        if (zipExists || releasedSize > 0) {
            console.log(`[ZipLoader] 已释放 bundle '${bundleName}': ${this.formatBytes(releasedSize)}, 剩余缓存: ${this.formatBytes(this.currentCacheSize)}`);
            return true;
        }
        
        console.warn(`[ZipLoader] bundle '${bundleName}' 不存在或已释放`);
        return false;
    }
    
    // 释放所有缓存
    unloadAllBundles(): void {
        const bundleNames = Object.keys(this.bundleZipMap);
        console.log(`[ZipLoader] 正在释放所有 ${bundleNames.length} 个bundle...`);
        
        bundleNames.forEach(bundleName => {
            this.unloadZipBundle(bundleName);
        });
        
        // 清空下载任务
        this.downloadTasks.clear();
        this.currentDownloads = 0;
        this.currentCacheSize = 0;
        
        console.log(`[ZipLoader] 所有bundle已释放`);
    }
    
    // 获取缓存统计信息
    getCacheStats() {
        const bundleCount = Object.keys(this.bundleZipMap).length;
        const fileCount = Object.values(this.bundleFileMaps)
            .reduce((total, files) => total + Object.keys(files).length, 0);
        
        return {
            bundleCount,
            fileCount,
            cacheSize: this.currentCacheSize,
            cacheSizeFormatted: this.formatBytes(this.currentCacheSize),
            maxCacheSize: MAX_CACHE_SIZE,
            maxCacheSizeFormatted: this.formatBytes(MAX_CACHE_SIZE),
            cacheUsagePercent: ((this.currentCacheSize / MAX_CACHE_SIZE) * 100).toFixed(1) + '%',
            currentDownloads: this.currentDownloads,
            downloadTasks: this.downloadTasks.size
        };
    }
    
    // 清理资源（组件销毁时调用）
    destroy(): void {
        // 清理定时器
        if (this.autoReleaseTimer) {
            clearInterval(this.autoReleaseTimer);
            this.autoReleaseTimer = undefined;
        }
        
        // 释放所有资源
        this.unloadAllBundles();
        
        console.log(`[ZipLoader] ZipBundleLoader 已销毁`);
    }
}


export const ZipLoader = ZipBundleLoader.getInstance();